package com.dfe.backend.common.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.dfe.backend.common.bean.real.RealData;
import com.dfe.backend.common.bean.real.RealLink;
import com.dfe.backend.common.bean.real.RealNode;
import com.dfe.backend.common.bean.real.circuit.SpdCircuit;
import com.dfe.backend.common.bean.real.circuit.SpdCircuitLink;
import com.dfe.backend.common.bean.real.circuit.SpdCircuitNode;
import com.dfe.backend.common.bean.real.circuit.SpdNextNode;
import com.dfe.backend.common.bean.real.cubicle.CableInfo;
import com.dfe.backend.common.bean.real.device.DeviceData;
import com.dfe.backend.common.bean.real.device.DeviceLink;
import com.dfe.backend.common.bean.real.device.DeviceNode;
import com.dfe.backend.common.bean.virtual.VirtualLink;
import com.dfe.backend.common.bean.virtual.VirtualNode;
import com.dfe.backend.common.config.ApplicationContextConfig;
import com.dfe.backend.common.constant.CableEnum;
import com.dfe.backend.common.constant.DeviceEnum;
import com.dfe.backend.common.sax.ScdServiceHandler;
import com.dfe.backend.common.sax.SpdServiceHandler;
import com.dfe.backend.common.sax.scd.ScdData;
import com.dfe.backend.common.sax.scd.ied.*;
import com.dfe.backend.common.sax.spd.SpdData;
import com.dfe.backend.common.sax.spd.substation.Bay;
import com.dfe.backend.common.sax.spd.substation.Region;
import com.dfe.backend.common.sax.spd.substation.Substation;
import com.dfe.backend.common.sax.spd.substation.bay.Cable;
import com.dfe.backend.common.sax.spd.substation.region.Core;
import com.dfe.backend.common.sax.spd.substation.region.Cubicle;
import com.dfe.backend.common.sax.spd.substation.region.device.Component;
import com.dfe.backend.common.sax.spd.substation.region.device.Device;
import com.dfe.backend.common.sax.spd.substation.region.device.SubDevice;
import com.dfe.backend.common.websocket.client.WebSocketClient;
import lombok.extern.slf4j.Slf4j;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 【描述】：
 *
 * @author LiChang
 * @since 2025-7-31 14:01
 */
@Slf4j
public class XmlUtil {

    /**
     * 【描述】: 解析SPD文件
     *
     * @param spdPath spd文件路径
     * @return {@link SpdData}
     * @author LiChang
     * @since 2025-7-31 16:06
     */
    public static SpdData analyseSpd(String spdPath) {
        try {
            // 路径判定
            File file = new File(spdPath);
            // 读取 xml 文件
            InputStream inputStream = Files.newInputStream(file.toPath());
            // 开始解析
            SAXParserFactory parserFactory = SAXParserFactory.newInstance();
            SAXParser parser = parserFactory.newSAXParser();
            SpdServiceHandler myHandler = new SpdServiceHandler();
            parser.parse(inputStream, myHandler);
            SpdData spdData = myHandler.spdData;
            if (spdData != null) {
                // 屏柜基础信息集合
                Map<String, Device> deviceMap = spdData.getDeviceMap();
                Map<String, Cubicle> cubicleMap = spdData.getCubicleMap();
                // 线缆详情集合
                Map<String, List<CableInfo>> cableDetailsMap = new HashMap<>();
                // 初始化设备列表
                List<DeviceNode> deviceNodeList = new ArrayList<>();
                List<DeviceLink> deviceLinkList = new ArrayList<>();
                List<String> existDeviceList = new ArrayList<>();
                // 初始化屏柜列表
                List<RealNode> realNodeList = new ArrayList<>();
                List<RealLink> realLinkList = new ArrayList<>();
                // 厂站
                Substation substation = spdData.getSubstation();
                // 多线程处理开始
                log.info("==============多线程处理分析=================");
                // 区域
                for (Region region : substation.getRegionList()) {
                    // 屏柜
                    for (Cubicle cubicle : region.getCubicleList()) {
                        // 屏柜基础信息
                        String cubicleName = cubicle.getName();
                        String cubicleDesc = cubicle.getDesc();
                        for (Core core : cubicle.getCoreList()) {
                            // 封装设备信息
                            String startDeviceName = core.getStartDeviceName();
                            String endDeviceName = core.getEndDeviceName();
                            if (StrUtil.isEmpty(startDeviceName) || StrUtil.isEmpty(endDeviceName)) {
                                // 如果任意为空，则舍弃
                                log.error("屏柜连接线【{}】的信息为空！", cubicleName + "." + core.getName());
                                continue;
                            }
                            Device startDevice = deviceMap.get(cubicleName + "." + startDeviceName);
                            Device endDevice = deviceMap.get(cubicleName + "." + endDeviceName);
                            if (startDevice == null || endDevice == null) {
                                // 如果任意屏柜信息为空，则舍弃
                                log.error("设备信息为空！请留意开始设备为【{}】，结束设备为【{}】的信息是否存在", startDeviceName, endDeviceName);
                                continue;
                            }
                            if (!existDeviceList.contains(startDeviceName)) {
                                packageDeviceNode(cubicleName, cubicleDesc, startDevice, deviceNodeList);
                                existDeviceList.add(startDeviceName);
                            }
                            if (!existDeviceList.contains(endDeviceName)) {
                                packageDeviceNode(cubicleName, cubicleDesc, endDevice, deviceNodeList);
                                existDeviceList.add(endDeviceName);
                            }
                            if (!startDeviceName.equals(endDeviceName)) {
                                packageDeviceLink(startDeviceName, endDeviceName, cubicleName, deviceLinkList);
                            }
                        }
                    }
                    DeviceData deviceData = new DeviceData();
                    deviceData.setLinkKeyProperty("device");
                    deviceData.setNodeDataArray(deviceNodeList);
                    deviceData.setLinkDataArray(deviceLinkList);
                    spdData.setDeviceData(deviceData);
                }
                // 开始屏柜关系分析，受Bay中Cable影响
                for (Bay bay : substation.getBayList()) {
                    // 受Cable影响，无需在意Core
                    for (Cable cable : bay.getCableList()) {
                        // 开始分析全站网络拓扑功能
                        String startCubicleName = cable.getStartCubicleName();
                        String endCubicleName = cable.getEndCubicleName();
                        if (StrUtil.isEmpty(startCubicleName) || StrUtil.isEmpty(endCubicleName)) {
                            // 如果任意为空，则舍弃
                            log.error("线缆【{}】的屏柜信息为空！", cable.getName());
                            continue;
                        }
                        // 开始整理分析进入缓存，需区分出具体光缆缓存，和屏柜间的联系
                        Cubicle startCubicle = cubicleMap.get(startCubicleName);
                        Cubicle endCubicle = cubicleMap.get(endCubicleName);
                        if (startCubicle == null || endCubicle == null) {
                            // 如果任意屏柜信息为空，则舍弃
                            log.error("屏柜信息为空！请留意开始屏柜为【{}】，结束屏柜为【{}】的信息是否存在", startCubicleName, endCubicleName);
                            continue;
                        }
                        // 创建Key
                        String key = startCubicleName + endCubicleName;
                        List<CableInfo> cableInfoList = cableDetailsMap.get(key);
                        if (cableInfoList == null) {
                            cableInfoList = new ArrayList<>();
                            // 只有首次，封装Real信息
                            packageRealNode(startCubicle, realNodeList);
                            packageRealNode(endCubicle, realNodeList);
                            packageRealLink(startCubicleName, endCubicleName, bay, realLinkList);
                        }
                        packageCableInfo(cable, cableInfoList, startCubicle.getDesc(), endCubicle.getDesc());
                        cableDetailsMap.put(key, cableInfoList);
                    }
                }
                // 封装RealData，全站屏柜拓扑
                RealData realData = new RealData();
                realData.setLinkKeyProperty("key");
                realData.setNodeDataArray(realNodeList);
                realData.setLinkDataArray(realLinkList);
                // 封装SPD信息
                spdData.setRealData(realData);
                spdData.setCableDetailsMap(cableDetailsMap);

                Map<String, List<SpdCircuit>> spdCircuitMap = analyseSpdCircuit(substation, deviceMap);
                spdData.setSpdCircuitMap(spdCircuitMap);
            }
            return spdData;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 【描述】: 分析接收端起点的设备全路径
     *
     * @param substation 厂站信息
     * @param deviceMap  设备信息集合
     * @author LiChang
     * @since 2025-10-13 17:06
     */
    public static Map<String, List<SpdCircuit>> analyseSpdCircuit(Substation substation, Map<String, Device> deviceMap) {
        // CubicleName,List<Cable>集合
        Map<String, List<Cable>> cubicleCableMap = new HashMap<>();
        Map<String, List<Core>> cubicleCoreMap = new HashMap<>();
        // 区域
        for (Region region : substation.getRegionList()) {
            // 屏柜
            for (Cubicle cubicle : region.getCubicleList()) {
                // 屏柜基础信息
                String cubicleName = cubicle.getName();
                for (Core core : cubicle.getCoreList()) {
                    if (cubicleCoreMap.containsKey(cubicleName)) {
                        List<Core> coreList = cubicleCoreMap.get(cubicleName);
                        coreList.add(core);
                    } else {
                        List<Core> coreList = new ArrayList<>();
                        coreList.add(core);
                        cubicleCoreMap.put(cubicleName, coreList);
                    }
                }
            }
        }
        for (Bay bay : substation.getBayList()) {
            for (Cable cable : bay.getCableList()) {
                // 存储屏柜与线材的关系
                saveCubicleCable(cable.getStartCubicleName(), cable, cubicleCableMap);
                saveCubicleCable(cable.getEndCubicleName(), cable, cubicleCableMap);
            }
        }
        // 初始化结果
        Map<String, List<SpdCircuit>> spdCircuitMap = new HashMap<>();
        // 开始遍历接收端设备，先决条件：接收端设备是IED设备，接收端为RX，可用作起点
        for (Region region : substation.getRegionList()) {
            // 屏柜
            for (Cubicle cubicle : region.getCubicleList()) {
                // 屏柜基础信息
                String cubicleName = cubicle.getName();
                String cubicleDesc = cubicle.getDesc();
                for (Core core : cubicle.getCoreList()) {
                    // 初始化结果集
                    List<SpdCircuit> spdCircuitList = new ArrayList<>();

                    String startDeviceName = core.getStartDeviceName();
                    String startSubDeviceName = core.getStartSubDeviceName();

                    String endDeviceName = core.getEndDeviceName();
                    String endSubDeviceName = core.getEndSubDeviceName();
                    // 用于判断是否为接收端
                    String startPinName = core.getStartPinName();
                    String endPinName = core.getEndPinName();
                    // 用于判断设备是否为IED
                    Device startDevice = deviceMap.get(cubicleName + "." + startDeviceName);
                    Device endDevice = deviceMap.get(cubicleName + "." + endDeviceName);
                    if (startDevice != null && endDevice != null && ("RX".equalsIgnoreCase(startPinName) || "RX".equalsIgnoreCase(endPinName))) {
                        // 双设备不为空，且有一端必须为IED设备
                        if (StrUtil.isNotEmpty(startDevice.getIedName()) || StrUtil.isNotEmpty(endDevice.getIedName())) {
                            // 初始化信息
                            SpdCircuit spdCircuit = new SpdCircuit();
                            List<SpdCircuitNode> circuitNodeList = new ArrayList<>();
                            List<SpdCircuitLink> circuitLinkList = new ArrayList<>();
                            // 开始溯源
                            if ("TX".equalsIgnoreCase(startPinName) || "TX".equalsIgnoreCase(endPinName)) {
                                // 端口信息
                                String startPort = getSubDevicePort(startSubDeviceName, startDevice, startPinName);
                                String endPort = getSubDevicePort(endSubDeviceName, endDevice, endPinName);
                                if (StrUtil.isEmpty(startPort) || StrUtil.isEmpty(endPort)) {
                                    // 端口号如果为空，则无视此次链接
                                    continue;
                                }
                                // key 格式为cubicleName:deviceName.subDeviceName.pinName:port
                                String startKey = cubicleName + ":" + startDeviceName + "." + startSubDeviceName + "." + startPinName + ":" + startPort;
                                String endKey = cubicleName + ":" + endDeviceName + "." + endSubDeviceName + "." + endPinName + ":" + endPort;
                                // 初始化key
                                String key;
                                // 不同设备情况，但直连
                                if ("RX".equalsIgnoreCase(startPinName)) {
                                    // 开始为接收端
                                    key = endDevice.getIedName() + ":" + startDevice.getIedName() + "." + startPort;
                                    spdCircuit = packageSpdCircuit(cubicleName, cubicleName, endDevice, startDevice, endKey, startKey, "内部连接");
                                } else {
                                    // 结束为接收端
                                    key = startDevice.getIedName() + ":" + endDevice.getIedName() + "." + endPort;
                                    spdCircuit = packageSpdCircuit(cubicleName, cubicleName, startDevice, endDevice, startKey, endKey, "内部连接");
                                }
                                spdCircuitList.add(spdCircuit);
                                spdCircuitMap.put(key, spdCircuitList);
                            } else {
                                // 不为一次性连接结束，先找内部链接
                                String startPort = getSubDevicePort(startSubDeviceName, startDevice, startPinName);
                                if (StrUtil.isEmpty(startPort)) {
                                    // 端口号如果为空，则无视此次链接
                                    continue;
                                }
                                // key 格式为cubicleName:deviceName.subDeviceName.pinName:port
                                String startKey = cubicleName + ":" + startDeviceName + "." + startSubDeviceName + "." + startPinName + ":" + startPort;
                                String endKey = cubicleName + ":" + endDeviceName + "." + endPinName;
                                spdCircuit = packageSpdCircuit(cubicleName, cubicleName, endDevice, startDevice, endKey, startKey, "内部连接");
                                spdCircuitList.add(spdCircuit);
                                // 已存在设备列表
                                List<String> existDeviceList = new ArrayList<>();
                                existDeviceList.add(cubicleName + "." + startDeviceName);
                                // 外部链接最终节点
                                SpdNextNode finalExternalSpdNode = new SpdNextNode();
                                // 首次外部链接
                                SpdNextNode nextSpdNode = getExternalCircuit(cubicleName, endDeviceName, endPinName, deviceMap, spdCircuitList, cubicleCableMap, existDeviceList);
                                while (nextSpdNode != null) {
                                    // 复制节点信息至最终节点
                                    BeanUtil.copyProperties(nextSpdNode, finalExternalSpdNode);
                                    nextSpdNode = getExternalCircuit(cubicleName, nextSpdNode.getDeviceName(), nextSpdNode.getPinName(), deviceMap, spdCircuitList, cubicleCableMap, existDeviceList);
                                }
                                // TODO 交换机相关
                                // 内部回路相关
                                SpdNextNode finalNode = getInnerCircuit(finalExternalSpdNode.getCubicleName(), finalExternalSpdNode.getDeviceName(), finalExternalSpdNode.getPinName(), deviceMap, spdCircuitList, cubicleCoreMap, existDeviceList);
                                if (finalNode != null) {
                                    String key = finalNode.getIedName() + ":" + startDevice.getIedName() + "." + startPort;
                                    spdCircuitMap.put(key, spdCircuitList);
                                }
                            }
                        }
                    }
                }
            }
        }
        return spdCircuitMap;
    }

    /**
     * 【描述】: 封装设备节点
     *
     * @param cubicleName    屏柜名称
     * @param cubicleDesc    屏柜描述
     * @param device         device
     * @param deviceNodeList deviceNodeList
     * @author LiChang
     * @since 2025-9-23 16:36
     */
    public static void packageDeviceNode(String cubicleName, String cubicleDesc, Device device, List<DeviceNode> deviceNodeList) {
        DeviceNode deviceNode = new DeviceNode();
        deviceNode.setKey(cubicleName + "." + device.getName());
        deviceNode.setName(device.getName());
        deviceNode.setDesc(device.getDesc());
        deviceNode.setCubicleName(cubicleName);
        deviceNode.setCubicleDesc(cubicleDesc);
        // 设备类型
        String deviceClass = device.getClassName();
        if (StrUtil.isNotEmpty(deviceClass)) {
            deviceNode.setDeviceClass(DeviceEnum.getDeviceClassDesc(deviceClass));
        } else {
            deviceNode.setDeviceClass("未知类型");
        }
        deviceNodeList.add(deviceNode);
    }

    /**
     * 【描述】: 封装设备连接
     *
     * @param startDeviceName startDeviceName
     * @param endDeviceName   endDeviceName
     * @param cubicleName     cubicleName
     * @param deviceLinkList  deviceLinkList
     * @author LiChang
     * @since 2025-9-23 17:18
     */
    public static void packageDeviceLink(String startDeviceName, String endDeviceName, String cubicleName, List<DeviceLink> deviceLinkList) {
        DeviceLink deviceLink = new DeviceLink();
        deviceLink.setKey(cubicleName + "." + startDeviceName + endDeviceName);
        deviceLink.setFrom(cubicleName + "." + startDeviceName);
        deviceLink.setTo(cubicleName + "." + endDeviceName);
        deviceLink.setName(startDeviceName + endDeviceName);
        deviceLink.setDesc(deviceLink.getName());
        deviceLink.setStatus(1);
        deviceLinkList.add(deviceLink);
    }

    /**
     * 【描述】: 封装线缆信息
     *
     * @param cable            cable
     * @param cableInfoList    cableInfoList
     * @param startCubicleDesc startCubicleDesc
     * @param endCubicleDesc   endCubicleDesc
     * @author LiChang
     * @since 2025-9-18 17:22
     */
    public static void packageCableInfo(Cable cable, List<CableInfo> cableInfoList, String startCubicleDesc, String endCubicleDesc) {
        CableInfo cableInfo = new CableInfo();
        cableInfo.setName(cable.getName());
        cableInfo.setDesc(cable.getDesc());
        // 线缆类型
        String className = cable.getClassName();
        if (StrUtil.isNotEmpty(className)) {
            cableInfo.setClassDesc(CableEnum.getCableTypeDesc(className));
        } else {
            cableInfo.setClassDesc("未知类型");
        }
        cableInfo.setType(cable.getType());
        cableInfo.setLength(cable.getLength());
        cableInfo.setCoreNum(cable.getCoreNum());
        cableInfo.setCrossSection(cable.getCrossSection());
        cableInfo.setStartCubicleDesc(startCubicleDesc);
        cableInfo.setEndCubicleDesc(endCubicleDesc);
        cableInfoList.add(cableInfo);
    }

    /**
     * 【描述】: 封装屏柜数据
     *
     * @param cubicle      cubicle
     * @param realNodeList realNodeList
     * @author LiChang
     * @since 2025-9-18 16:36
     */
    public static void packageRealNode(Cubicle cubicle, List<RealNode> realNodeList) {
        RealNode realNode = new RealNode();
        realNode.setKey(cubicle.getName());
        realNode.setName(cubicle.getName());
        realNode.setDesc(cubicle.getDesc());
        realNode.setCategory(cubicle.getClassName());
        realNodeList.add(realNode);
    }

    /**
     * 【描述】: 封装屏柜关系数据
     *
     * @param startCubicleName startCubicleName
     * @param endCubicleName   endCubicleName
     * @param bay              bay
     * @param realLinkList     realLinkList
     * @author LiChang
     * @since 2025-9-18 16:36
     */
    public static void packageRealLink(String startCubicleName, String endCubicleName, Bay bay, List<RealLink> realLinkList) {
        RealLink realLink = new RealLink();
        realLink.setKey(startCubicleName + endCubicleName);
        realLink.setFrom(startCubicleName);
        realLink.setTo(endCubicleName);
        realLink.setName(bay.getName());
        realLink.setDesc(bay.getDesc());
        realLinkList.add(realLink);
    }

    /**
     * 【描述】: 解析SCD文件
     *
     * @param scdPath scdPath
     * @return {@link SpdData}
     * @author LiChang
     * @since 2025-7-31 16:06
     */
    public static ScdData analyseScd(String scdPath) {
        try {
            // 路径判定
            File file = new File(scdPath);
            // 读取 xml 文件
            InputStream inputStream = Files.newInputStream(file.toPath());
            // 开始解析
            SAXParserFactory parserFactory = SAXParserFactory.newInstance();
            SAXParser parser = parserFactory.newSAXParser();
            ScdServiceHandler myHandler = new ScdServiceHandler();
            parser.parse(inputStream, myHandler);
            // 开始发送端描述
            ScdData scdData = myHandler.configData;
            if (scdData != null) {
                WebSocketClient webSocketClient = ApplicationContextConfig.getBean(WebSocketClient.class);
                Map<String, Object> jsonParam = new HashMap<>();
                jsonParam.put("iedName", scdData.getIedNameList());
                jsonParam.put("optype", "ied");
                // Map转json String
                webSocketClient.sendMessage(JSONUtil.toJsonStr(jsonParam));

                // 先处理数据集匹配
                // IED和数据集，cbName定位关系
                Map<String, Map<String, String>> iedDataSetGseMap = scdData.getIedDataSetGseMap();
                Map<String, Map<String, String>> iedDataSetSvMap = scdData.getIedDataSetSvMap();
                Map<String, String> pathToDataSetMap = scdData.getPathToDataSetMap();
                Map<String, String> referencePathMap = scdData.getReferencePathMap();
                Map<String, String> appIdMap = scdData.getAppIdMap();
                // 获取全部IED列表
                List<Ied> iedList = scdData.getIedList();
                // 获取全路径分析
                List<ExtRef> allExtRefList = scdData.getAllExtRefList();
                // 最终路径
                List<ExtRef> finalExtRefList = new ArrayList<>();
                // 根据发送端iedName进行筛选
                Map<String, List<ExtRef>> tempExtRefMap = allExtRefList.stream().filter(ref -> ref.getIedName() != null).collect(Collectors.groupingBy(ExtRef::getIedName));
                tempExtRefMap.forEach((iedName, extRefList) -> {
                    // 遍历Map，开始发送端匹配
                    for (Ied ied : iedList) {
                        // 匹配到发送端
                        if (ied.getName().equals(iedName)) {
                            forExtRef:
                            for (ExtRef extRef : extRefList) {
                                // 开始发送端描述匹配[iedName][ldInst].[prefix][lnClass][lnInst].doName.daName
                                for (AccessPoint accessPoint : ied.getAccessPointList()) {
                                    for (LDevice lDevice : accessPoint.getLDeviceList()) {
                                        // 符合LDevice inst描述
                                        if (lDevice.getInst().equals(extRef.getLdInst())) {
                                            // 特定LN0处理
                                            LN0 ln0 = lDevice.getLn0();
                                            if (ln0.getLnClass().equals(extRef.getLnClass()) && "LLN0".equalsIgnoreCase(ln0.getLnClass())) {
                                                // 为特定LN0，需进行特殊判定
                                                for (DOI doi : ln0.getDoiList()) {
                                                    if (doi.getName().equals(extRef.getDoName())) {
                                                        packageExtRefData(doi, extRef, accessPoint, iedName, lDevice, iedDataSetGseMap, iedDataSetSvMap, pathToDataSetMap, referencePathMap, appIdMap);
                                                        continue forExtRef; // 结束本次ExtRef层遍历
                                                    }
                                                }
                                            }
                                            for (LN ln : lDevice.getLnList()) {
                                                if (ln.getPrefix().equals(extRef.getPrefix()) && ln.getLnClass().equals(extRef.getLnClass()) && ln.getInst().equals(extRef.getLnInst())) {
                                                    for (DOI doi : ln.getDoiList()) {
                                                        if (doi.getName().equals(extRef.getDoName())) {
                                                            packageExtRefData(doi, extRef, accessPoint, iedName, lDevice, iedDataSetGseMap, iedDataSetSvMap, pathToDataSetMap, referencePathMap, appIdMap);
                                                            continue forExtRef; // 结束本次ExtRef层遍历
                                                        }
                                                    }
                                                    break; // 结束LN层遍历
                                                }
                                            }
                                            break; // 结束LDevice层遍历
                                        }
                                    }
                                }
                            }
                            break; // 结束IED遍历
                        }
                    }
                    finalExtRefList.addAll(extRefList);
                });
                scdData.setAllExtRefList(finalExtRefList);

                // 获取IED名称列表
                List<String> iedNameList = scdData.getIedNameList();
                Map<String, String> iedNameMap = scdData.getIedNameMap();
                Map<String, List<ExtRef>> iedExtRefMap = scdData.getIedExtRefMap();

                // 封装IED及路径关系Map
                for (ExtRef extRef : finalExtRefList) {
                    String sendIedName = extRef.getIedName();
                    String receiveIedName = extRef.getReceiveIedName();
                    if (sendIedName.equals(receiveIedName)) {
                        iedExtRefMap.get(sendIedName).add(extRef);
                    } else {
                        if (iedExtRefMap.containsKey(sendIedName)) {
                            iedExtRefMap.get(sendIedName).add(extRef);
                        }
                        if (iedExtRefMap.containsKey(receiveIedName)) {
                            iedExtRefMap.get(receiveIedName).add(extRef);
                        }
                    }
                }
                // 开始虚拟结构构建
                Map<String, List<VirtualNode>> iedVirtualNodeMap = new HashMap<>();
                // 真实端口与APPID映射
                Map<String, String> realPortMap = new HashMap<>();
                for (String iedName : iedNameList) {
                    // 每个节点的临时Map
                    Map<String, VirtualNode> tempVirtualNodeMap = new HashMap<>();
                    for (ExtRef extRef : iedExtRefMap.get(iedName)) {
                        String sendIedName = extRef.getIedName();
                        String receiveIedName = extRef.getReceiveIedName();
                        // 跳过自身
                        if (sendIedName.equals(receiveIedName)) {
                            continue;
                        }
                        // 端口KEY， 发送端IED + "." + 接收端IED + "." + APPID
                        String portKey = sendIedName + "." + receiveIedName + "." + extRef.getAppId();
                        if (!realPortMap.containsKey(portKey)) {
                            realPortMap.put(portKey, extRef.getReceivePort());
                        }
                        // 初始化虚回路信息
                        VirtualLink virtualLink = new VirtualLink();
                        virtualLink.setAppId(extRef.getAppId());
                        virtualLink.setSendDescription(extRef.getSendDesc());
                        virtualLink.setReceiveDescription(extRef.getReceiveDesc());
                        virtualLink.setValue("0"); // 初始默认值为0
                        virtualLink.setStatus(1); //默认连通
                        // 声明对端IED
                        String offsideIedName;
                        if (sendIedName.equals(iedName)) {
                            // 自身为发送端
                            offsideIedName = receiveIedName;
                            virtualLink.setType(1);
                        } else {
                            // 自身为接收端
                            offsideIedName = sendIedName;
                            virtualLink.setType(2);
                        }
                        // num为发送端参考路径
                        virtualLink.setNum(extRef.getSendReferencePath());
                        // 获取虚拟节点
                        VirtualNode virtualNode = tempVirtualNodeMap.get(offsideIedName);
                        if (virtualNode == null) {
                            virtualNode = new VirtualNode();
                            virtualNode.setIedName(offsideIedName);
                            virtualNode.setDescription(iedNameMap.get(offsideIedName));
                            virtualNode.setLinks(new ArrayList<>());
                        }
                        // 构建虚回路信息
                        List<VirtualLink> links = virtualNode.getLinks();
                        links.add(virtualLink);
                        virtualNode.setLinks(links);
                        tempVirtualNodeMap.put(offsideIedName, virtualNode);
                    }
                    List<VirtualNode> virtualNodeList = new ArrayList<>(tempVirtualNodeMap.values());
                    iedVirtualNodeMap.put(iedName, virtualNodeList);
                }
                scdData.setIedVirtualNodeMap(iedVirtualNodeMap);
                scdData.setRealPortMap(realPortMap);
            }
            return scdData;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 【描述】: 封装ExtRef数据
     *
     * @param doi              doi
     * @param extRef           extRef
     * @param accessPoint      accessPoint
     * @param iedName          iedName
     * @param lDevice          lDevice
     * @param iedDataSetGseMap iedDataSetGseMap
     * @param iedDataSetSvMap  iedDataSetSvMap
     * @param pathToDataSetMap pathToDataSetMap
     * @param referencePathMap referencePathMap
     * @param appIdMap         appIdMap
     * @author LiChang
     * @since 2025-8-29 14:43
     */
    private static void packageExtRefData(DOI doi, ExtRef extRef, AccessPoint accessPoint, String iedName, LDevice lDevice, Map<String, Map<String, String>> iedDataSetGseMap, Map<String, Map<String, String>> iedDataSetSvMap, Map<String, String> pathToDataSetMap, Map<String, String> referencePathMap, Map<String, String> appIdMap) {
        //  节点赋值用于获取APPID
        extRef.setAccessPointName(accessPoint.getName());
        // 获取数据集名称，[iedName][ldInst].[prefix][lnClass][lnInst].doName.daName
        String dataSetKey = iedName + extRef.getLdInst() + "." + extRef.getPrefix() + extRef.getLnClass() + extRef.getLnInst() + "." + extRef.getDoName();
        if (StrUtil.isNotEmpty(extRef.getDaName())) {
            dataSetKey = dataSetKey + "." + extRef.getDaName();
        }
        String dataSetName = pathToDataSetMap.get(dataSetKey);
        String referencePath = referencePathMap.get(dataSetKey);
        if (StrUtil.isNotEmpty(referencePath)) {
            extRef.setSendReferencePath(referencePath);
        }
        if (StrUtil.isNotEmpty(dataSetName)) {
            String iedDataSetKey = iedName + "." + accessPoint.getName();
            Map<String, String> dataSetMap = iedDataSetGseMap.get(iedDataSetKey);
            if (iedDataSetGseMap.get(iedDataSetKey) == null) {
                dataSetMap = iedDataSetSvMap.get(iedDataSetKey);
            }
            // 根据数据集名称获取cbName
            String cbName = dataSetMap.get(dataSetName);
            // 确定AppId
            String key = iedName + "." + accessPoint.getName() + "." + cbName + "." + lDevice.getInst();
            extRef.setAppId(appIdMap.get(key));
            extRef.setSendDesc(doi.getDesc());
//            for (DAI dai : doi.getDaiList()) {
//                if ("dU".equals(dai.getName())) {
//                    extRef.setSendDesc(dai.getVal().getValue());
//                    break;
//                }
//            }
        }
    }

    /**
     * 【描述】: 获取子设备的端口
     *
     * @param subDeviceName 子设备名称
     * @param device        设备信息
     * @param pinName       端点信息
     * @return {@link String}
     * @author LiChang
     * @since 2025-10-16 15:56
     */
    public static String getSubDevicePort(String subDeviceName, Device device, String pinName) {
        for (SubDevice subDevice : device.getSubDeviceList()) {
            if (subDevice.getName().equals(subDeviceName)) {
                // 找到对应的设备
                for (Component component : subDevice.getComponentList()) {
                    if (component.getName().equalsIgnoreCase(pinName)) {
                        return component.getIcdPort();
                    }
                }
            }
        }
        return "";
    }

    /**
     * 【描述】: 外部链接回路（非交换机）
     *
     * @param cubicleName     屏柜名称
     * @param deviceName      一端设备名
     * @param pinName         一端端点名
     * @param deviceMap       设备集合
     * @param spdCircuitList  SPD回路
     * @param cubicleCableMap 屏柜与线缆集合
     * @param existDeviceList 已遍历设备
     * @return {@link SpdNextNode}
     * @author LiChang
     * @since 2025-10-22 14:14
     */
    public static SpdNextNode getExternalCircuit(String cubicleName, String deviceName, String pinName, Map<String, Device> deviceMap, List<SpdCircuit> spdCircuitList, Map<String, List<Cable>> cubicleCableMap, List<String> existDeviceList) {
        // 获取屏柜与线材关系
        List<Cable> cableList = cubicleCableMap.get(cubicleName);
        if (cableList != null && !cableList.isEmpty()) {
            for (Cable cable : cableList) {
                // 存在屏柜名称相同，开始遍历线材中的连接
                for (Core core : cable.getCoreList()) {
                    if ((cable.getStartCubicleName().equals(cubicleName) && core.getStartDeviceName().equals(deviceName) && core.getStartPinName().equals(pinName)) || (cable.getEndCubicleName().equals(cubicleName) && core.getEndDeviceName().equals(deviceName) && core.getEndPinName().equals(pinName))) {
                        // 构建返回结果
                        SpdNextNode spdNextNode = new SpdNextNode();
                        // 判断是否为已经过设备
                        if (existDeviceList.contains(cable.getStartCubicleName() + "." + core.getStartDeviceName()) || existDeviceList.contains(cable.getEndCubicleName() + "." + core.getEndDeviceName())) {
                            // 已存在，返回
                            continue;
                        }
                        String startCubicleName = cable.getStartCubicleName();
                        String endCubicleName = cable.getEndCubicleName();
                        Device startDevice = deviceMap.get(startCubicleName + "." + core.getStartDeviceName());
                        Device endDevice = deviceMap.get(endCubicleName + "." + core.getEndDeviceName());
                        // key 格式为cubicleName:deviceName.subDeviceName.pinName:port
                        String startKey = startCubicleName + ":" + core.getStartDeviceName() + "." + core.getStartPinName();
                        String endKey = endCubicleName + ":" + core.getEndDeviceName() + "." + core.getEndPinName();
                        // 找到对应外部链接，开始创建
                        SpdCircuit spdCircuit;
                        if (startCubicleName.equals(cubicleName)) {
                            // 开始端为接收端
                            spdCircuit = packageSpdCircuit(endCubicleName, startCubicleName, endDevice, startDevice, endKey, startKey, CableEnum.getCableTypeDesc(cable.getClassName()));
                            spdNextNode.setCubicleName(endCubicleName);
                            spdNextNode.setDeviceName(core.getEndDeviceName());
                            spdNextNode.setPinName(core.getEndPinName());
                            // 已存在设备添加，格式为cubicleName + "." + deviceName
                            existDeviceList.add(startCubicleName + "." + core.getStartDeviceName());
                        } else {
                            // 开始端为发送端
                            spdCircuit = packageSpdCircuit(startCubicleName, endCubicleName, startDevice, endDevice, startKey, endKey, CableEnum.getCableTypeDesc(cable.getClassName()));
                            spdNextNode.setCubicleName(startCubicleName);
                            spdNextNode.setDeviceName(core.getStartDeviceName());
                            spdNextNode.setPinName(core.getStartPinName());
                            existDeviceList.add(endCubicleName + "." + core.getEndDeviceName());
                        }
                        // 原档信息处理
                        if (spdCircuitList != null && !spdCircuitList.isEmpty()) {
                            // 因为向上查询，所以全部节点信息+1
                            for (SpdCircuit tempCircuit : spdCircuitList) {
                                tempCircuit.setNode(tempCircuit.getNode() + 1);
                            }
                            spdCircuitList.add(spdCircuit);
                        }
                        spdNextNode.setExistDeviceList(existDeviceList);
                        return spdNextNode;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 【描述】: 交换机实回路
     *
     * @param substation substation
     * @param deviceMap  deviceMap
     * @author LiChang
     * @since 2025-10-20 10:37
     */
    public static void getSwitchCircuit(Substation substation, Map<String, Device> deviceMap) {
        //
    }

    /**
     * 【描述】: 分析内部回路
     *
     * @param cubicleName     屏柜名称
     * @param deviceName      一端设备名
     * @param pinName         一端端点名
     * @param deviceMap       设备集合
     * @param spdCircuitList  SPD回路
     * @param cubicleCoreMap  屏柜内连接集合
     * @param existDeviceList 已遍历设备
     * @author LiChang
     * @since 2025-10-23 10:46
     */
    public static SpdNextNode getInnerCircuit(String cubicleName, String deviceName, String pinName, Map<String, Device> deviceMap, List<SpdCircuit> spdCircuitList, Map<String, List<Core>> cubicleCoreMap, List<String> existDeviceList) {
        // 获取屏柜与线材关系
        List<Core> coreList = cubicleCoreMap.get(cubicleName);
        // 存在屏柜名称相同，开始遍历线材中的连接
        if (coreList != null && !coreList.isEmpty()) {
            for (Core core : coreList) {
                if ((core.getStartDeviceName().equals(deviceName) && core.getStartPinName().equals(pinName)) || (core.getEndDeviceName().equals(deviceName) && core.getEndPinName().equals(pinName))) {
                    // 基础信息
                    String startDeviceName = core.getStartDeviceName();
                    String startSubDeviceName = core.getStartSubDeviceName();
                    String endDeviceName = core.getEndDeviceName();
                    String endSubDeviceName = core.getEndSubDeviceName();
                    // 用于判断是否为接收端
                    String startPinName = core.getStartPinName();
                    String endPinName = core.getEndPinName();
                    // 用于判断设备是否为IED
                    Device startDevice = deviceMap.get(cubicleName + "." + startDeviceName);
                    Device endDevice = deviceMap.get(cubicleName + "." + endDeviceName);
                    // 构建返回结果
                    SpdNextNode spdNextNode = new SpdNextNode();
                    // 判断是否为已经过设备
                    if (existDeviceList.contains(cubicleName + "." + startDeviceName) || existDeviceList.contains(cubicleName + "." + endDeviceName)) {
                        // 已存在，返回
                        continue;
                    }
                    // 端口信息
                    String startPort = getSubDevicePort(startSubDeviceName, startDevice, startPinName);
                    String endPort = getSubDevicePort(endSubDeviceName, endDevice, endPinName);
                    if (("TX".equalsIgnoreCase(startPinName) && !"RX".equalsIgnoreCase(endPinName)) || ("TX".equalsIgnoreCase(endPinName) && !"RX".equalsIgnoreCase(startPinName))) {
                        // 单TX，无接收端情况，为寻找最开始节点
                        SpdCircuit spdCircuit;
                        if ("TX".equalsIgnoreCase(startPinName)) {
                            String startKey = cubicleName + ":" + startDeviceName + "." + startSubDeviceName + "." + startPinName + ":" + startPort;
                            String endKey = cubicleName + ":" + endDeviceName + "." + endPinName;
                            // 开始为最开始节点
                            spdCircuit = packageSpdCircuit(cubicleName, cubicleName, startDevice, endDevice, startKey, endKey, "内部连接");
                            spdNextNode.setIedName(startDevice.getIedName());
                        } else {
                            String startKey = cubicleName + ":" + startDeviceName + "." + startPinName;
                            String endKey = cubicleName + ":" + endDeviceName + "." + endSubDeviceName + "." + endPinName + ":" + endPort;
                            // 结束为最开始节点
                            spdCircuit = packageSpdCircuit(cubicleName, cubicleName, endDevice, startDevice, endKey, startKey, "内部连接");
                            spdNextNode.setIedName(endDevice.getIedName());
                        }
                        // 原档信息处理
                        if (spdCircuitList != null && !spdCircuitList.isEmpty()) {
                            // 因为向上查询，所以全部节点信息+1
                            for (SpdCircuit tempCircuit : spdCircuitList) {
                                tempCircuit.setNode(tempCircuit.getNode() + 1);
                            }
                            spdCircuitList.add(spdCircuit);
                        }
                        return spdNextNode;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 【描述】: 存储屏柜和线材的集合
     *
     * @param cubicleName     屏柜名
     * @param cable           线材信息
     * @param cubicleCableMap 屏柜线材集合
     * @author LiChang
     * @since 2025-10-21 14:27
     */
    public static void saveCubicleCable(String cubicleName, Cable cable, Map<String, List<Cable>> cubicleCableMap) {
        if (cubicleCableMap.containsKey(cubicleName)) {
            List<Cable> cableList = cubicleCableMap.get(cubicleName);
            cableList.add(cable);
        } else {
            List<Cable> cableList = new ArrayList<>();
            cableList.add(cable);
            cubicleCableMap.put(cubicleName, cableList);
        }
    }

    /**
     * 【描述】: 封装SPD回路信息
     *
     * @param sendCubicleName    sendCubicleName
     * @param receiveCubicleName receiveCubicleName
     * @param sendDevice         sendDevice
     * @param receiveDevice      receiveDevice
     * @param sendKey            sendKey
     * @param receiveKey         receiveKey
     * @param linkDesc           linkDesc
     * @return {@link SpdCircuit}
     * @author LiChang
     * @since 2025-10-21 15:06
     */
    public static SpdCircuit packageSpdCircuit(String sendCubicleName, String receiveCubicleName, Device sendDevice, Device receiveDevice, String sendKey, String receiveKey, String linkDesc) {
        SpdCircuit spdCircuit = new SpdCircuit();
        spdCircuit.setNode(1);
        spdCircuit.setStartDeviceDesc(sendDevice.getDesc());
        spdCircuit.setEndDeviceDesc(receiveDevice.getDesc());
        spdCircuit.setSendKey(sendKey);
        spdCircuit.setReceiveKey(receiveKey);
        spdCircuit.setSendType(sendDevice.getClassName());
        spdCircuit.setReceiveType(receiveDevice.getClassName());
        spdCircuit.setSendCubicleName(sendCubicleName);
        spdCircuit.setReceiveCubicleName(receiveCubicleName);
        spdCircuit.setLinkDesc(linkDesc);
        return spdCircuit;
    }

    public static SpdCircuit packageCircuitNodeLinks(String sendCubicleName, String receiveCubicleName, Device sendDevice, Device receiveDevice, String sendKey, String receiveKey, String startPort, String endPort, String linkDesc, List<SpdCircuitNode> circuitNodeList, List<SpdCircuitLink> circuitLinkList) {
        if (circuitNodeList != null && !circuitNodeList.isEmpty()) {
            // 存在已有节点，需确定最后一个节点
        } else {
            // 不存在节点，创建双端Node和Link
            SpdCircuitNode sendNode = packageCircuitNode(sendDevice, sendKey);
            SpdCircuitNode receiveNode = packageCircuitNode(receiveDevice, receiveKey);

            sendNode.setFromPort(startPort);
            receiveNode.setToPort(endPort);


        }
        SpdCircuit spdCircuit = new SpdCircuit();
        spdCircuit.setNode(1);
        spdCircuit.setStartDeviceDesc(sendDevice.getDesc());
        spdCircuit.setEndDeviceDesc(receiveDevice.getDesc());
        spdCircuit.setSendKey(sendKey);
        spdCircuit.setReceiveKey(receiveKey);
        spdCircuit.setSendType(sendDevice.getClassName());
        spdCircuit.setReceiveType(receiveDevice.getClassName());
        spdCircuit.setSendCubicleName(sendCubicleName);
        spdCircuit.setReceiveCubicleName(receiveCubicleName);
        spdCircuit.setLinkDesc(linkDesc);
        return spdCircuit;
    }

    /**
     * 【描述】: 封装SPD回路节点信息
     *
     * @param device 设备
     * @param key    key
     * @return {@link SpdCircuitNode}
     * @author LiChang
     * @since 2025-10-28 16:55
     */
    public static SpdCircuitNode packageCircuitNode(Device device, String key) {
        SpdCircuitNode node = new SpdCircuitNode();
        node.setKey(key);
        node.setName(key);
        node.setDesc(device.getDesc());
        node.setCategory(device.getClassName());
        node.setFromPortStatus(0);
        node.setToPortStatus(0);
        return node;
    }


    public static void main(String[] args) {
        try {
            // 记录开始时间
            Instant start = Instant.now();
//            String scdPath = "F:\\gitSpace\\online-monitoring-of-secondary-equipment\\文档\\国网山东泰安供电公司110kV安家变电站综自及交直流改造逻辑模型.spd";
            String scdPath = "F:\\全部项目文档\\自动化\\2025\\二次设备在线监测\\substation.scd";
            ScdData scdData = analyseScd(scdPath);
            // 计算时间差
            Duration duration = Duration.between(start, Instant.now());

            // 输出结果（支持多种时间单位）
            System.out.println("执行时间: " + duration.toMillis() + " 毫秒");
            System.out.println("执行完毕");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
